feat(cli): residual-diff between two branches (#5)#41
Conversation
…#5) Compute the per-branch residual difference as a finding_id set diff (finding_id is branch/commit-stable, #12 L1), so "what this branch added/removed" needs no PR entity, new index, or storage change. - branch_residual: ResidualDiff dataclass + pure residual_diff(base, head) helper (added = head − base, removed = base − head, unchanged_count = |base ∩ head|). - scan.py: `residual-diff --repo --base --head` subcommand reusing residual_for_repo (single snapshot read); fail-closed when a branch has no residual (unscanned ≠ "0 added"); dynamodb-only, exit 2 guard like `residual`. Spec: docs/workbench/specs/residual-diff/ (research-grounded self-Q&A). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces the residual-diff subcommand and helper functions to compute and display the difference in residual findings (added, removed, and unchanged secrets) between two branches of a repository. It includes design specifications, domain models, CLI integration, and comprehensive tests. The review feedback suggests deduplicating the missing branches list in the error message when both --base and --head refer to the same missing branch.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
When --base and --head are the same missing branch, report it once instead of twice (dict.fromkeys dedupe). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
What
Add
residual-diff— the difference in residual secrets between two branches,computed as a finding_id set diff. No PR entity, new index, or storage change.
Why
finding_idis branch/commit-stable (#12 L1), so "what this branch added/removed"is just
residual(head) − residual(base)/residual(base) − residual(head)overthe existing per-branch residual. A PR is just a branch pair, so no PR-specific
machinery is needed.
Changes
runtime/branch_residual.py:ResidualDiffdataclass + pureresidual_diff(base, head)helper (
added/removedsorted,unchanged_count).cli/commands/scan.py:residual-diff --repo --base --headreusingresidual_for_repofor a single consistent snapshot read; fail-closed when abranch has no residual (an unscanned branch is not "0 added");
dynamodb-onlywith exit 2, mirroring
residual.docs/workbench/specs/residual-diff/(research-grounded self-Q&A).Test
uv run pytest— 684 passed (helper added/removed/unchanged/directional;CLI diff, missing-branch exit 2, jsonl exit 2).
governance.public_safetyclean.Related to #23 follow-on residual work.